# coding:utf-8

import os
import os.path as op
from math import ceil
from datetime import date, timedelta
from flask import redirect, request, flash
from flask_admin import Admin, expose, AdminIndexView, form
from flask_admin.contrib.sqla import ModelView
from flask_admin.contrib.sqla.view import func
from flask_admin.base import MenuLink
from flask_admin.babel import gettext
from sqlalchemy.event import listens_for
from sqlalchemy.orm import joinedload

from .. import db
from config import basedir
from ..main.models import Material
from ..helpers import get_mdict_item_or_list, get_redirect_target


class MyAdminView(AdminIndexView):
    @expose('/')
    def index(self):
        args = u"请点击 MPS -> Material 查看物品列表"
        return self.render('admin.html', args=args)
        # return redirect('http://127.0.0.1:5000/material')


class MaterialView(ModelView):
    column_filters = ('name', 'location', 'keywords', 'description', 'expiration')
    can_delete = True
    can_create = False
    can_view_details = True
    details_modal = False
    details_template = 'details.html'
    list_template = 'list.html'

    column_labels = {
        'name': u'名称',
        'location': u'位置',
        'storage_time': u'存储时间',
        'keywords': u'关键字',
        'description': u'描述',
        'expiration': u'过期时间',
        'picture': u'图片',
    }
    column_editable_list = (
        'name', 'location', 'storage_time', 'keywords', 'description', 'expiration')  # 可以在list视图下直接修改的
    column_list = ('name', 'location', 'storage_time', 'keywords', 'description', 'expiration')
    # column_exclude_list = ('picture')
    form_excluded_columns = ()  # 不在创建与修改视图下显示的列
    column_searchable_list = ('name', 'location', 'keywords', 'description')

    # def _list_thumbnail(view, context, model, name):
    # if not model.picture:
    # return ''
    # return Markup(form.thumbgen_filename(model.picture))

    # column_formatters = {
    # 'picture': _list_thumbnail
    # }
    form_extra_fields = {
        'picture': form.ImageUploadField(u'图片', base_path=op.join(basedir, 'app', 'photos'),
                                         thumbnail_size=(100, 100, True), endpoint='images')
    }
    '''
    form_overrides = {
        'picture':form.ImageUploadField
    }
    form_args = {
        'picture':{
            'label':u'图片',
            'base_path': op.join(basedir,'app','photos'),
        }
    }
    '''

    def _get_query(self):
        return self.session.query(self.model).filter(self.model.expiration < date.today() + timedelta(days=-30))

    def _get_count_query(self):
        return self.session.query(func.count('*')).filter(self.model.expiration < date.today() + timedelta(days=-30))

    @expose('/detail', methods=['POST', 'GET'])
    def details_view(self):
        return_url = get_redirect_target() or self.get_url('.index_view')
        if not self.can_view_details:
            return redirect(return_url)

        id = get_mdict_item_or_list(request.args, 'id')
        if id is None:
            return redirect(return_url)

        model = self.get_one(id)

        if model is None:
            flash(gettext('Record does not exist.'), 'error')
            return redirect(return_url)

        if self.details_modal and request.args.get('modal'):
            template = self.details_modal_template
        else:
            template = self.details_template

        return self.render(template,
                           model=model,
                           details_columns=self._details_columns,
                           get_value=self.get_list_value,
                           return_url=return_url)

    # URL generation helpers
    def _get_expiration_list_url(self, view_args):
        """
            Generate page URL with current page, sort column and
            other parameters.

            :param view:
                View name
            :param view_args:
                ViewArgs object with page number, filters, etc.
        """
        page = view_args.page or None
        desc = 1 if view_args.sort_desc else None

        kwargs = dict(page=page, sort=view_args.sort, desc=desc, search=view_args.search)
        kwargs.update(view_args.extra_args)

        if view_args.page_size:
            kwargs['page_size'] = view_args.page_size

        kwargs.update(self._get_filters(view_args.filters))

        return self.get_url('.exp', **kwargs)

    def _get_list(self, page, sort_column, sort_desc, search, filters,
                  execute=True, page_size=None):
        """
            Return records from the database.

            :param page:
                Page number
            :param sort_column:
                Sort column name
            :param sort_desc:
                Descending or ascending sort
            :param search:
                Search query
            :param execute:
                Execute query immediately? Default is `True`
            :param filters:
                List of filter tuples
            :param page_size:
                Number of results. Defaults to ModelView's page_size. Can be
                overriden to change the page_size limit. Removing the page_size
                limit requires setting page_size to 0 or False.
        """

        # Will contain join paths with optional aliased object
        joins = {}
        count_joins = {}

        query = self._get_query()
        count_query = self._get_count_query() if not self.simple_list_pager else None

        # Ignore eager-loaded relations (prevent unnecessary joins)
        # TODO: Separate join detection for query and count query?
        if hasattr(query, '_join_entities'):
            for entity in query._join_entities:
                for table in entity.tables:
                    joins[table] = None

        # Apply search criteria
        if self._search_supported and search:
            query, count_query, joins, count_joins = self._apply_search(query,
                                                                        count_query,
                                                                        joins,
                                                                        count_joins,
                                                                        search)

        # Apply filters
        if filters and self._filters:
            query, count_query, joins, count_joins = self._apply_filters(query,
                                                                         count_query,
                                                                         joins,
                                                                         count_joins,
                                                                         filters)

        # Calculate number of rows if necessary
        count = count_query.scalar() if count_query else None

        # Auto join
        for j in self._auto_joins:
            query = query.options(joinedload(j))

        # Sorting
        query, joins = self._apply_sorting(query, joins, sort_column, sort_desc)

        # Pagination
        query = self._apply_pagination(query, page, page_size)

        # Execute if needed
        if execute:
            query = query.all()

        return count, query

    @expose('/expiration')
    def exp(self):
        """
            List view
        """
        if self.can_delete:
            delete_form = self.delete_form()
        else:
            delete_form = None

        # Grab parameters from URL
        view_args = self._get_list_extra_args()

        # Map column index to column name
        sort_column = self._get_column_by_idx(view_args.sort)
        if sort_column is not None:
            sort_column = sort_column[0]

        # Get page size
        page_size = view_args.page_size or self.page_size

        # Get count and data
        count, data = self._get_list(view_args.page, sort_column, view_args.sort_desc,
                                     view_args.search, view_args.filters, page_size=page_size)

        list_forms = {}
        if self.column_editable_list:
            for row in data:
                list_forms[self.get_pk_value(row)] = self.list_form(obj=row)

        # Calculate number of pages
        if count is not None and page_size:
            num_pages = int(ceil(count / float(page_size)))
        elif not page_size:
            num_pages = 0  # hide pager for unlimited page_size
        else:
            num_pages = None  # use simple pager

        # Various URL generation helpers
        def pager_url(p):
            # Do not add page number if it is first page
            if p == 0:
                p = None

            return self._get_expiration_list_url(view_args.clone(page=p))

        def sort_url(column, invert=False, desc=None):
            if not desc and invert and not view_args.sort_desc:
                desc = 1

            return self._get_expiration_list_url(view_args.clone(sort=column, sort_desc=desc))

        def page_size_url(s):
            if not s:
                s = self.page_size

            return self._get_expiration_list_url(view_args.clone(page_size=s))

        # Actions
        actions, actions_confirmation = self.get_actions_list()
        if actions:
            action_form = self.action_form()
        else:
            action_form = None

        clear_search_url = self._get_expiration_list_url(view_args.clone(page=0,
                                                                         sort=view_args.sort,
                                                                         sort_desc=view_args.sort_desc,
                                                                         search=None,
                                                                         filters=None))

        return self.render(
            self.list_template,
            data=data,
            list_forms=list_forms,
            delete_form=delete_form,
            action_form=action_form,

            # List
            list_columns=self._list_columns,
            sortable_columns=self._sortable_columns,
            editable_columns=self.column_editable_list,
            list_row_actions=self.get_list_row_actions(),

            # Pagination
            count=count,
            pager_url=pager_url,
            num_pages=num_pages,
            can_set_page_size=self.can_set_page_size,
            page_size_url=page_size_url,
            page=view_args.page,
            page_size=page_size,
            default_page_size=self.page_size,

            # Sorting
            sort_column=view_args.sort,
            sort_desc=view_args.sort_desc,
            sort_url=sort_url,

            # Search
            search_supported=self._search_supported,
            clear_search_url=clear_search_url,
            search=view_args.search,

            # Filters
            filters=self._filters,
            filter_groups=self._get_filter_groups(),
            active_filters=view_args.filters,
            filter_args=self._get_filters(view_args.filters),

            # Actions
            actions=actions,
            actions_confirmation=actions_confirmation,

            # Misc
            enumerate=enumerate,
            get_pk_value=self.get_pk_value,
            get_value=self.get_list_value,
            return_url=self._get_expiration_list_url(view_args),
        )


@listens_for(Material, 'after_delete')
def del_image(mapper, connection, target):
    if target.picture:
        # 删除图片
        try:
            os.remove(op.join(basedir, 'app', 'photos', target.picture))
        except OSError:
            pass
        # 删除thumbnail
        try:
            os.remove(op.join(basedir, 'app', 'photos', form.thumbgen_filename(target.picture)))
        except OSError:
            pass


admin = Admin(name=u"管理", index_view=MyAdminView(name=u"管理主页", url='/'), template_mode="bootstrap3")

admin.add_view(MaterialView(Material, db.session, category="MPS", endpoint='material'))

admin.add_link(MenuLink(u"过期物品", endpoint='material.exp', icon_type='glyph', icon_value='glyphicon glyphicon-time'))
admin.add_link(
    MenuLink(u"批量增加", endpoint='main.batch_add', icon_type='glyph', icon_value='glyphicon glyphicon-text-height'))
admin.add_link(MenuLink(u"增加", endpoint='main.add', icon_type='glyph', icon_value='glyphicon glyphicon-plus'))

